package com.zlx.mall.basic.datasource.generator;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.baomidou.mybatisplus.generator.query.SQLQuery;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author qingke
 **/
@Data
@Accessors(chain = true)
public class MybatisGenerator {

    private static final String MYSQL_TYPE_DATETIME = "datetime";
    private static final String MYSQL_TYPE_TIMESTAMP = "timestamp";

    private String url;
    private String username;
    private String password;
    private String author;

    private String projectPath;
    private String parentPackageName;
    private List<String> includes;
    private String mapper = "mapper";

    private boolean entityFileOverride = true;
    private boolean serviceFileOverride = true;
    private boolean mapperFileOverride = true;

    public void setInclude(String include) {
        if (this.includes == null || this.includes.size() == 0) {
            this.includes = new ArrayList<>();
        }
        this.includes.add(include);
    }

    /**
     * Parse MySQLWorkBench Copy Short Names
     *
     * @param shortNames e.g. `table_a`, `table_b`, `table_c`, `table_d`, `table_e`
     */
    public void parseMySqlWorkBenchShortNames(String shortNames) {
        List<String> tables = Arrays.stream(StringUtils.tokenizeToStringArray(
                        StringUtils.trimAllWhitespace(
                                StringUtils.delete(shortNames, "`")), ","))
                .distinct()
                .collect(Collectors.toList());

        if (tables.size() > 0) {
            if (this.includes == null || this.includes.size() == 0) {
                this.includes = new ArrayList<>();
            }
            this.includes.addAll(tables);
        }
    }

    public void start() {
        // Data Source
        FastAutoGenerator
                .create(new DataSourceConfig.Builder(url, username, password)
                        .databaseQueryClass(SQLQuery.class)
                        .typeConvert((config, type) -> {
                            String dateType = type.replaceAll("\\(\\d+\\)", "").toLowerCase();
                            if (dateType.contains(MYSQL_TYPE_DATETIME)) {
                                return CustomerDbColumnType.LOCAL_DATE_TIME;
                            }
                            if (dateType.contains(MYSQL_TYPE_TIMESTAMP)) {
                                return CustomerDbColumnType.INSTANT;
                            }
                            return MySqlTypeConvert.INSTANCE.processTypeConvert(config, type);
                        }))

                // Global Config
                .globalConfig(builder ->
                        builder.author(author)
                                .disableOpenDir()
                                .dateType(DateType.TIME_PACK)
                                .outputDir(projectPath + "/src/main/java"))

                // Package Config
                .packageConfig(builder ->
                        builder.parent(parentPackageName)
                                .mapper(mapper)
                                .pathInfo(Collections.singletonMap(
                                        OutputFile.xml, projectPath +
                                                "/src/main/resources/" +
                                                parentPackageName.replaceAll("\\.", "/") +
                                                "/" + mapper + "/"))
                )

                // Strategy Config
                .strategyConfig(builder -> {
                            builder.addInclude(includes);
                            // entity
                            if (entityFileOverride) {
                                builder.entityBuilder()
                                        .enableFileOverride();
                            }
                            builder.entityBuilder()
                                    .enableColumnConstant()
                                    .enableTableFieldAnnotation()
                                    .enableLombok()
                                    .enableChainModel();
                            // mapper
                            if (mapperFileOverride) {
                                builder.mapperBuilder()
                                        .enableFileOverride();
                            }
                            builder.mapperBuilder()
                                    .enableBaseResultMap()
                                    .enableBaseColumnList();
                            // service
                            if (serviceFileOverride) {
                                builder.serviceBuilder()
                                        .enableFileOverride();
                            }

                        }
                )

                // Injection Config

                // Template Config
                .templateConfig(builder ->
                        builder.disable(TemplateType.CONTROLLER)
                                .entity("/mybatis/template/entity.java")
                                .xml("/mybatis/template/mapper.xml"))

                // Template Engine
                .templateEngine(new FreemarkerTemplateEngine())

                .execute();
    }

    enum CustomerDbColumnType implements IColumnType {
        // 基本类型
        BASE_BYTE("byte", null),
        BASE_SHORT("short", null),
        BASE_CHAR("char", null),
        BASE_INT("int", null),
        BASE_LONG("long", null),
        BASE_FLOAT("float", null),
        BASE_DOUBLE("double", null),
        BASE_BOOLEAN("boolean", null),

        // 包装类型
        BYTE("Byte", null),
        SHORT("Short", null),
        CHARACTER("Character", null),
        INTEGER("Integer", null),
        LONG("Long", null),
        FLOAT("Float", null),
        DOUBLE("Double", null),
        BOOLEAN("Boolean", null),
        STRING("String", null),

        // sql 包下数据类型
        DATE_SQL("Date", "java.sql.Date"),
        TIME("Time", "java.sql.Time"),
        TIMESTAMP("Timestamp", "java.sql.Timestamp"),
        BLOB("Blob", "java.sql.Blob"),
        CLOB("Clob", "java.sql.Clob"),

        // java8 新时间类型
        LOCAL_DATE("LocalDate", "java.time.LocalDate"),
        LOCAL_TIME("LocalTime", "java.time.LocalTime"),
        YEAR("Year", "java.time.Year"),
        YEAR_MONTH("YearMonth", "java.time.YearMonth"),
        LOCAL_DATE_TIME("LocalDateTime", "java.time.LocalDateTime"),
        // 自定义补充
        ZONED_DATE_TIME("ZonedDateTime", "java.time.ZonedDateTime"),
        INSTANT("Instant", "java.time.Instant"),

        // 其他杂类
        MAP("Map", "java.util.Map"),
        BYTE_ARRAY("byte[]", null),
        OBJECT("Object", null),
        DATE("Date", "java.util.Date"),
        BIG_INTEGER("BigInteger", "java.math.BigInteger"),
        BIG_DECIMAL("BigDecimal", "java.math.BigDecimal");

        /**
         * 类型
         */
        private final String type;

        /**
         * 包路径
         */
        private final String pkg;

        CustomerDbColumnType(final String type, final String pkg) {
            this.type = type;
            this.pkg = pkg;
        }

        @Override
        public String getType() {
            return type;
        }

        @Override
        public String getPkg() {
            return pkg;
        }
    }
}